package org.chen.scene.service;

import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
public class WebScrapingService {

    private static final int TIMEOUT = 8000; // 减少到8秒，提高并发效率
    private static final int MAX_RETRIES = 2; // 添加重试机制

    // 添加更多User-Agent，避免被反爬
    private static final String[] USER_AGENTS = {
            // Windows - Chrome 最新版
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",

            // Windows - Edge
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.60",

            // Windows - Firefox
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:117.0) Gecko/20100101 Firefox/117.0",

            // macOS - Safari
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Safari/605.1.15",

            // macOS - Chrome
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",

            // iPhone - Safari
            "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Mobile/15E148 Safari/604.1",

            // Android - Chrome
            "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36",

            // Android - Firefox
            "Mozilla/5.0 (Android 13; Mobile; rv:117.0) Gecko/117.0 Firefox/117.0"
    };


    /**
     * 抓取景点详细信息 - 线程安全版本，带重试机制
     * @param detailUrl 景点详情页URL
     * @return 包含介绍、开放时间和图片的Map
     */
    public Map<String, String> scrapeAttractionDetails(String detailUrl) {
        Map<String, String> details = new HashMap<>();
        details.put("introduction", "");
        details.put("openingHours", "");
        details.put("images", "");

        if (detailUrl == null || detailUrl.trim().isEmpty()) {
            log.warn("详情页URL为空");
            return details;
        }

        String threadName = Thread.currentThread().getName();
        log.debug("开始抓取景点详情: {} [线程: {}]", detailUrl, threadName);

        // 带重试的抓取
        for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
            try {
                Document doc = connectWithRetry(detailUrl, attempt);
                if (doc != null) {
                    extractAllDetails(doc, details);

                    log.debug("抓取完成 [线程: {}] - 介绍: {}字, 开放时间: {}字, 图片: {}个",
                            threadName,
                            details.get("introduction").length(),
                            details.get("openingHours").length(),
                            countImages(details.get("images")));
                    break;
                }
            } catch (Exception e) {
                log.warn("第{}次抓取失败 [线程: {}]: {} - {}",
                        attempt, threadName, detailUrl, e.getMessage());

                if (attempt == MAX_RETRIES) {
                    log.error("抓取景点详情最终失败 [线程: {}]: {}", threadName, detailUrl, e);
                } else {
                    // 重试前短暂等待
                    try {
                        Thread.sleep(1000 * attempt); // 递增等待时间
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
        }

        return details;
    }

    /**
     * 连接并获取文档，带重试逻辑
     */
    private Document connectWithRetry(String url, int attempt) throws IOException {
        // 随机选择User-Agent，避免被识别为爬虫
        String userAgent = USER_AGENTS[attempt % USER_AGENTS.length];

        return Jsoup.connect(url)
                .userAgent(userAgent)
                .timeout(TIMEOUT)
                .followRedirects(true)
                .ignoreHttpErrors(false) // 严格处理HTTP错误
                .maxBodySize(0) // 不限制页面大小
                .get();
    }

    /**
     * 提取所有详细信息的主方法
     */
    private void extractAllDetails(Document doc, Map<String, String> details) {
        // 方法1: 使用精确的选择器
        extractDetailsFromDetailModule(doc, details);

        // 方法2: 如果第一种方法没有找到足够信息，尝试备用方法
        if (needsMoreDetails(details)) {
            tryAlternativeSelectors(doc, details);
        }

        // 方法3: 最后的兜底方案
        if (needsMoreDetails(details)) {
            tryFallbackExtraction(doc, details);
        }
    }

    /**
     * 判断是否需要更多详细信息
     */
    private boolean needsMoreDetails(Map<String, String> details) {
        return details.get("introduction").length() < 50 ||
                details.get("openingHours").isEmpty();
    }

    /**
     * 从detailModule中提取信息（优化版本）
     */
    private void extractDetailsFromDetailModule(Document doc, Map<String, String> details) {
        Elements detailModules = doc.select(".detailModule");

        if (detailModules.isEmpty()) {
            log.debug("未找到.detailModule元素");
            return;
        }

        for (Element module : detailModules) {
            Elements titles = module.select(".moduleTitle");
            Elements contents = module.select(".moduleContent");

            log.debug("模块中找到 {} 个标题, {} 个内容", titles.size(), contents.size());

            for (int i = 0; i < Math.min(titles.size(), contents.size()); i++) {
                processModulePair(titles.get(i), contents.get(i), details);

                // 如果已经找到足够信息，提前退出
                if (!needsMoreDetails(details)) {
                    return;
                }
            }
        }
    }

    /**
     * 处理单个模块标题-内容对
     */
    private void processModulePair(Element titleElement, Element contentElement, Map<String, String> details) {
        String title = titleElement.text().trim();
        log.debug("处理模块标题: {}", title);

        if (isIntroductionTitle(title) && details.get("introduction").length() < 50) {
            String introduction = extractIntroductionContent(contentElement);
            String images = extractImages(contentElement);

            if (introduction.length() > details.get("introduction").length()) {
                details.put("introduction", introduction.startsWith("Copyright") ? "" : introduction);
                details.put("images", images);

                log.debug("更新景点介绍: 标题={}, 内容长度={}, 图片数量={}",
                        title, introduction.length(), countImages(images));
            }
        } else if (isOpeningHoursTitle(title) && details.get("openingHours").isEmpty()) {
            String openingHours = contentElement.text().trim();
            if (!openingHours.isEmpty()) {
                details.put("openingHours", openingHours);
                log.debug("找到开放时间: 标题={}, 内容={}", title, openingHours);
            }
        }
    }

    /**
     * 兜底提取方案
     */
    private void tryFallbackExtraction(Document doc, Map<String, String> details) {
        log.debug("使用兜底提取方案");

        // 如果还是没有介绍，尝试提取页面中较长的文本段落
        if (details.get("introduction").length() < 50) {
            extractLongestTextParagraph(doc, details);
        }

        // 如果还是没有开放时间，尝试查找包含时间格式的文本
        if (details.get("openingHours").isEmpty()) {
            extractTimePattern(doc, details);
        }
    }

    /**
     * 提取页面中最长的文本段落作为介绍
     */
    private void extractLongestTextParagraph(Document doc, Map<String, String> details) {
        Elements paragraphs = doc.select("p, div.content, div.desc, div.description");
        String longestText = "";
        Element longestElement = null;

        for (Element p : paragraphs) {
            String text = p.text().trim();
            if (text.length() > longestText.length() && text.length() > 100) {
                longestText = text;
                longestElement = p;
            }
        }

        if (!longestText.isEmpty()) {
            details.put("introduction", longestText.startsWith("Copyright") ? "" : longestText);
            if (longestElement != null) {
                String images = extractImages(longestElement);
                details.put("images", images);
            }
            log.debug("通过最长段落找到介绍，长度: {}", longestText.length());
        }
    }

    /**
     * 通过正则表达式查找时间模式
     */
    private void extractTimePattern(Document doc, Map<String, String> details) {
        String bodyText = doc.body().text();

        // 查找时间模式，如 "09:00-17:00" 或 "9:00-17:00"
        java.util.regex.Pattern timePattern = java.util.regex.Pattern.compile(
                "(?:开放时间|营业时间|开门时间)[：:]?\\s*([0-9]{1,2}:[0-9]{2}\\s*[-~至到]\\s*[0-9]{1,2}:[0-9]{2})"
        );

        java.util.regex.Matcher matcher = timePattern.matcher(bodyText);
        if (matcher.find()) {
            String timeInfo = matcher.group(1);
            details.put("openingHours", timeInfo);
            log.debug("通过正则表达式找到开放时间: {}", timeInfo);
        }
    }

    /**
     * 计算图片数量
     */
    private int countImages(String images) {
        if (images == null || images.trim().isEmpty()) {
            return 0;
        }
        return images.split(",").length;
    }

    /**
     * 提取介绍内容（优化版本）
     */
    private String extractIntroductionContent(Element contentElement) {
        if (contentElement == null) {
            return "";
        }

        Element clonedElement = contentElement.clone();

        // 移除不需要的元素
        clonedElement.select("img, script, style, .ad, .advertisement").remove();

        String text = clonedElement.text().trim();

        // 简单的文本清理
        text = text.replaceAll("\\s+", " "); // 多个空白字符替换为单个空格
        text = text.replaceAll("^[\\s\\p{Punct}]+|[\\s\\p{Punct}]+$", ""); // 去除首尾标点和空格

        return text;
    }

    /**
     * 提取图片URL（优化版本）
     */
    private String extractImages(Element contentElement) {
        if (contentElement == null) {
            return "";
        }

        List<String> imageUrls = new ArrayList<>();
        Elements images = contentElement.select("img");

        for (Element img : images) {
            String src = img.attr("src");
            if (src == null || src.trim().isEmpty()) {
                src = img.attr("data-src"); // 尝试懒加载图片的src
            }

            if (src != null && !src.trim().isEmpty() && isValidImageUrl(src)) {
                // 处理相对URL
                if (src.startsWith("//")) {
                    src = "https:" + src;
                } else if (src.startsWith("/")) {
                    // 这里可能需要根据实际网站调整
                    src = "https://www.ctrip.com" + src;
                }
                imageUrls.add(src.trim());
            }
        }

        return String.join(",", imageUrls);
    }

    /**
     * 验证是否为有效的图片URL
     */
    private boolean isValidImageUrl(String url) {
        if (url == null || url.length() < 10) {
            return false;
        }

        String lowerUrl = url.toLowerCase();
        return lowerUrl.contains(".jpg") ||
                lowerUrl.contains(".jpeg") ||
                lowerUrl.contains(".png") ||
                lowerUrl.contains(".gif") ||
                lowerUrl.contains(".webp") ||
                lowerUrl.contains("image") ||
                lowerUrl.contains("photo");
    }

    // 保持原有的判断方法不变
    private boolean isIntroductionTitle(String title) {
        if (title == null) return false;
        String lowerTitle = title.toLowerCase();
        return lowerTitle.equals("介绍") ||
                lowerTitle.equals("简介") ||
                lowerTitle.equals("概述") ||
                lowerTitle.equals("描述") ||
                lowerTitle.equals("详情") ||
                lowerTitle.contains("景点介绍") ||
                lowerTitle.equals("attraction") ||
                lowerTitle.equals("introduction") ||
                lowerTitle.equals("description");
    }

    private boolean isOpeningHoursTitle(String title) {
        if (title == null) return false;
        String lowerTitle = title.toLowerCase();
        return lowerTitle.equals("开放时间") ||
                lowerTitle.equals("营业时间") ||
                lowerTitle.equals("开门时间") ||
                lowerTitle.equals("参观时间") ||
                lowerTitle.equals("opening hours") ||
                lowerTitle.equals("hours") ||
                lowerTitle.equals("opening time");
    }

    // 保持原有的备用方法，但添加性能优化
    private void tryAlternativeSelectors(Document doc, Map<String, String> details) {
        log.debug("使用备用选择器抓取数据");

        if (details.get("introduction").length() < 50) {
            tryFindByTextMatch(doc, details, "introduction");
        }

        if (details.get("openingHours").isEmpty()) {
            tryFindByTextMatch(doc, details, "openingHours");
        }

        if (details.get("introduction").length() < 50) {
            tryAlternativeIntroductionSelectors(doc, details);
        }

        if (details.get("openingHours").isEmpty()) {
            tryAlternativeOpeningHoursSelectors(doc, details);
        }
    }

    // 其他方法保持原样...
    private void tryFindByTextMatch(Document doc, Map<String, String> details, String type) {
        String searchText = type.equals("introduction") ? "介绍" : "开放时间";
        Elements matchingElements = doc.select("*:contains(" + searchText + ")");

        for (Element element : matchingElements) {
            if (isLikelyTitle(element, searchText)) {
                Element nextSibling = element.nextElementSibling();
                if (nextSibling != null) {
                    String content = nextSibling.text().trim();
                    if (content.length() > 10) {
                        if (type.equals("introduction")) {
                            details.put("introduction", content.startsWith("Copyright") ? "" : content);
                            String images = extractImages(nextSibling);
                            details.put("images", images);
                        } else {
                            details.put("openingHours", content);
                        }
                        log.debug("通过文本匹配找到{}: {}", searchText,
                                content.substring(0, Math.min(50, content.length())));
                        break;
                    }
                }
            }
        }
    }

    private boolean isLikelyTitle(Element element, String expectedText) {
        String text = element.ownText().trim();
        String tagName = element.tagName().toLowerCase();

        return text.equals(expectedText) &&
                (tagName.matches("h[1-6]") ||
                        element.hasClass("title") ||
                        element.hasClass("moduleTitle") ||
                        element.className().toLowerCase().contains("title"));
    }

    private void tryAlternativeIntroductionSelectors(Document doc, Map<String, String> details) {
        String[] selectors = {
                ".poi-intro", ".attraction-intro", ".description", ".summary",
                ".content-desc", ".poi-desc", ".attraction-description",
                ".LimitHeightText", ".moduleContent:first-of-type"
        };

        for (String selector : selectors) {
            Elements elements = doc.select(selector);
            if (!elements.isEmpty()) {
                String content = extractIntroductionContent(elements.first());
                if (!content.isEmpty() && content.length() > 10) {
                    details.put("introduction", content.startsWith("Copyright") ? "" : content);
                    String images = extractImages(elements.first());
                    details.put("images", images);
                    log.debug("通过备用选择器 {} 找到介绍内容，长度: {}", selector, content.length());
                    break;
                }
            }
        }
    }

    private void tryAlternativeOpeningHoursSelectors(Document doc, Map<String, String> details) {
        String[] selectors = {
                ".opening-hours", ".business-hours", ".time-info", ".hours",
                ".opening-time", ".visit-time"
        };

        for (String selector : selectors) {
            Elements elements = doc.select(selector);
            if (!elements.isEmpty()) {
                String content = elements.first().text().trim();
                if (!content.isEmpty()) {
                    details.put("openingHours", content);
                    log.debug("通过备用选择器 {} 找到开放时间: {}", selector, content);
                    break;
                }
            }
        }

        if (details.get("openingHours").isEmpty()) {
            Elements timeElements = doc.select("*:containsOwn(开放时间), *:containsOwn(营业时间), *:containsOwn(开门时间)");
            for (Element element : timeElements) {
                String text = element.text().trim();
                if (text.length() > 5 && text.length() < 200 &&
                        (text.contains(":") || text.contains("：") || text.contains("-"))) {
                    details.put("openingHours", text);
                    log.debug("通过文本搜索找到开放时间: {}", text);
                    break;
                }
            }
        }
    }
}